home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2010 April / PCWorld0410.iso / pluginy Firefox / 1833 / 1833.xpi / modules / yoonoCommandQueue.js < prev    next >
Text File  |  2009-12-16  |  15KB  |  503 lines

  1. var EXPORTED_SYMBOLS = ["commandQueueMgr"];
  2.  
  3. Components.utils.import("resource://yoono/yoonoPrefs.js");
  4.  
  5. // Yoono stuff
  6. var yoono = {};
  7. var log = {info:function() {},debug:function() {},warn:function(){},error:function (){},fatal:function(){}};
  8.  
  9. // Vars
  10. const YOONO_DIR = "yoono";
  11. const SYNC_FILE = "yoono_commands.log";
  12. const SYNC_RUNNING_FILE = "yoono_running_commands.log";
  13.  
  14.  
  15. // Globals
  16. const CI = Components.interfaces;
  17. const CL = Components.classes;
  18. const DIRSERVICE = CL['@mozilla.org/file/directory_service;1'].getService(CI.nsIProperties);
  19. const PREFSSERVICE = CL["@mozilla.org/preferences-service;1"].getService(CI.nsIPrefService);
  20. const PREFS = PREFSSERVICE.getBranch("extensions.yoono.");
  21.  
  22.  
  23. const REQUEST_DELAY = 1000;  // en ms.
  24.  
  25.  
  26. //**************************************************************************/
  27. //
  28. // All request can now pile up but only transactional are saved over sessions
  29. //
  30.  
  31. function CommandQueue() {
  32.     this.wrappedJSObject=this;
  33.     
  34.     
  35.     this._nextTransactionFile = DIRSERVICE.get("ProfDS", CI.nsIFile);
  36.     this._runningTransactionFile = DIRSERVICE.get("ProfDS", CI.nsIFile);
  37.     
  38.     this._nextTransactionFile.append(YOONO_DIR); this._nextTransactionFile.append(SYNC_FILE);
  39.     this._runningTransactionFile.append(YOONO_DIR); this._runningTransactionFile.append(SYNC_RUNNING_FILE);
  40.     
  41.     this._running=[];
  42.     this._next=[];
  43.     
  44.     this.isRunning=false;
  45.     
  46.     this._observers=[];     
  47.  
  48. }
  49.  
  50. CommandQueue.prototype.init = function (y) {
  51.  
  52.     yoono = y;
  53.     log = y.log;
  54.     try {
  55.         // Releases before 3.0.6 wrote the sync file in the user chrome directory
  56.         // Newer releases write the sync file in the yoono directory
  57.         // If the file exists at the old location, move it first
  58.         var oldFile = DIRSERVICE.get("UChrm", CI.nsIFile);
  59.         oldFile.append(SYNC_FILE);
  60.         if (oldFile.exists()) {
  61.             var newFile = DIRSERVICE.get('ProfDS', CI.nsIFile);
  62.             newFile.append(YOONO_DIR);
  63.             oldFile.moveTo(newFile,SYNC_FILE);
  64.         }
  65.         
  66.         
  67.         this.reload();
  68.     } catch(e) {
  69.         log.exception(e);
  70.     }
  71. }
  72.  
  73. CommandQueue.prototype.uninstall = function() {
  74.   try {
  75.     log.warn("Remove sync-file");
  76.     var file = DIRSERVICE.get("UChrm", CI.nsIFile);
  77.     file.append(SYNC_FILE);
  78.     if( file.exists() ) {
  79.       file.remove(true);
  80.     }
  81.   } catch(e) {}
  82.   try {
  83.     file = DIRSERVICE.get("ProfDS", CI.nsIFile);
  84.     file.append(YOONO_DIR);
  85.     file.append(SYNC_FILE);
  86.     if( file.exists() ) {
  87.       file.remove(true);
  88.     }
  89.   } catch(e) {}
  90.   try {
  91.     file = DIRSERVICE.get("ProfDS", CI.nsIFile);
  92.     file.append(YOONO_DIR);
  93.     file.append(SYNC_RUNNING_FILE);
  94.     if( file.exists() ) {
  95.       file.remove(true);
  96.     }
  97.   } catch(e) {}
  98. }
  99.  
  100. // Load commands from command file from last session.
  101. CommandQueue.prototype.reload = function() {
  102.     var start = (new Date()).getTime();
  103.     
  104.     var istream = CL["@mozilla.org/network/file-input-stream;1"].createInstance(CI.nsIFileInputStream);
  105.  
  106.     // Reload next requests
  107.     if( this._nextTransactionFile.exists() ) {
  108.         try {
  109.             istream.init(this._nextTransactionFile, 0x01, 0444, 0);
  110.             var converter = CL["@mozilla.org/intl/converter-input-stream;1"].createInstance( CI.nsIConverterInputStream);
  111.             converter.init(istream, 'UTF-8', 1024, '-');
  112.             var line = {};
  113.             this._next = [];
  114.             var content="";
  115.             while(converter.readString(4096, line)) {
  116.                 content+=line.value;
  117.             }
  118.             var requests=content.split('\n');
  119.             for each(var r in requests) {
  120.                 if (r.length<=3) continue;
  121.                 this._next.push( new XML(r) );
  122.             }
  123.             istream.close();
  124.         } catch(e) {
  125.             log.exception(e);
  126.             var outputStream = CL["@mozilla.org/network/file-output-stream;1"].createInstance( CI.nsIFileOutputStream);
  127.             outputStream.init(this._nextTransactionFile, 0x02 | 0x08 | 0x20 , 0644, 0);
  128.             outputStream.write("", 0);
  129.             outputStream.flush();
  130.             outputStream.close();
  131.         }
  132.     }
  133.     // Reload running requests
  134.     if( this._runningTransactionFile.exists() ) {
  135.         try {
  136.             istream.init(this._runningTransactionFile, 0x01, 0444, 0);
  137.             var converter = CL["@mozilla.org/intl/converter-input-stream;1"].createInstance( CI.nsIConverterInputStream);
  138.             converter.init(istream, 'UTF-8', 1024, '-');
  139.             var line = {};
  140.             this._running = [];
  141.             var content="";
  142.             while(converter.readString(4096, line)) {
  143.                 content+=line.value+"\n";
  144.             }
  145.             var requests=content.split('\n');
  146.             for each(var r in requests) {
  147.                 if (r.length<=3) continue;
  148.                 this._running.push( new XML(r) );
  149.             }
  150.             istream.close();
  151.         } catch(e) {
  152.             log.exception(e);
  153.             var outputStream = CL["@mozilla.org/network/file-output-stream;1"].createInstance( CI.nsIFileOutputStream);
  154.             outputStream.init(this._runningTransactionFile, 0x02 | 0x08 | 0x20 , 0644, 0);
  155.             outputStream.write("", 0);
  156.             outputStream.flush();
  157.             outputStream.close();
  158.         }
  159.     }
  160.     log.debug("reload elapse-time = "+ ((new Date()).getTime() - start));
  161.     
  162.     this.flush();
  163. }
  164.  
  165. // Simulate a browser shutdown for unit testing
  166. CommandQueue.prototype.simulateShutdown = function() {
  167.     this._running=[];
  168.     this._next=[];
  169. }
  170.  
  171. // Reset all list : in-memory and files
  172. // used mainly by unit testing
  173. CommandQueue.prototype.fullReset = function() {
  174.     this._running=[];
  175.     var outputStream = CL["@mozilla.org/network/file-output-stream;1"].createInstance( CI.nsIFileOutputStream);
  176.     outputStream.init(this._runningTransactionFile, 0x02 | 0x08 | 0x20 , 0644, 0);
  177.     outputStream.write("", 0);
  178.     outputStream.flush();
  179.     outputStream.close();
  180.     this._next=[];
  181.     var outputStream = CL["@mozilla.org/network/file-output-stream;1"].createInstance( CI.nsIFileOutputStream);
  182.     outputStream.init(this._nextTransactionFile, 0x02 | 0x08 | 0x20 , 0644, 0);
  183.     outputStream.write("", 0);
  184.     outputStream.flush();
  185.     outputStream.close();
  186.     
  187. }
  188.  
  189. // Register a new observer to handle responses and errors
  190. //   observer = {onSendingRequest:function(request){}, onRequestSuccess:function(request, response){return newRequest;}, onRequestError:function(request){}}
  191. //   if onRequestSuccess return a request (string), it is added to the current request and resent
  192. CommandQueue.prototype.addObserver = function(o) {
  193.     this._observers.push(o);
  194. }
  195. CommandQueue.prototype.removeObserver = function(o) {
  196.     for(i=0;i<this._observers.length;i++){
  197.         if(o==this._observers[i]) this._observers.splice(i, 1);
  198.     }
  199. }
  200.  
  201. // Request which must absolutely be sent
  202. // (equivalent of persistant next in old API)
  203. CommandQueue.prototype.addTransactionalRequest = function (s) {
  204.     
  205.     log.debug("Adding transactional request : "+s.toXMLString());
  206.     // Add to in-memory list
  207.         this._next.push(s)
  208.     
  209.     // Add to transaction file
  210.         var outputStream = CL["@mozilla.org/network/file-output-stream;1"].createInstance( CI.nsIFileOutputStream);
  211.         var converter = CL['@mozilla.org/intl/converter-output-stream;1'].createInstance(CI.nsIConverterOutputStream);
  212.         converter.init(outputStream, 'UTF-8', 1024, '-');
  213.         // WRONLY, CREATE_FILE, TRUNCATE
  214.         // from http://lxr.mozilla.org/aviary101branch/source/nsprpub/pr/include/prio.h
  215.         outputStream.init(this._nextTransactionFile, 0x02 | 0x08 | 0x10 , 0644, 0);
  216.         converter.writeString(s.toXMLString().replace(/\n/g,"")+"\n");
  217.         converter.flush();
  218.         outputStream.flush();
  219.         outputStream.close();
  220.     
  221.     // Flush requests
  222.         this.flush();
  223.     
  224. }
  225.  
  226. // All other requests which doesn't need to be keept over session
  227. // (equivalent of transcient next)
  228. CommandQueue.prototype.addTranscientRequest = function (s) {
  229.   var strCmd = s.toXMLString();
  230.     log.debug("Adding transcient request : " + strCmd);
  231.   // if connect command, remove any previous connect commands...
  232.   if(0 == strCmd.indexOf('<connect')) {
  233.     var nb = this._next.length;
  234.     for(var idx=nb-1; idx >= 0; idx --) {
  235.       var tr = this._next[idx];
  236.       strCmd = tr.toXMLString();
  237.       if(0 == strCmd.indexOf('<connect')) {
  238.         this._next.splice(idx, 1);
  239.       }
  240.     }
  241.   }
  242.     this._next.push(s);
  243.     this.flush();
  244.     
  245. }
  246.  
  247. // Replace current running requests with this one in order to run before next commands
  248. // (equivalent of transcient running with clearRunninCommands)
  249. CommandQueue.prototype._replaceRunningRequest = function (s) {
  250.     
  251.     log.debug("Replace request to the running one : "+s.toXMLString());
  252.     this._running=[s];
  253.     
  254. }
  255.  
  256. // Return the next request to send
  257. CommandQueue.prototype._getRequest = function () {
  258.     if (this._running.length==0) { // switch next requests to running ones
  259.         log.debug("Switch requests");
  260.         // 1) write next into running
  261.             // Switch in-memory lists
  262.                 this._running=this._next;
  263.             // Write next into running transaction file
  264.                 var outputStream = CL["@mozilla.org/network/file-output-stream;1"].createInstance( CI.nsIFileOutputStream);
  265.                 var converter = CL['@mozilla.org/intl/converter-output-stream;1'].createInstance(CI.nsIConverterOutputStream);
  266.                 converter.init(outputStream, 'UTF-8', 1024, '-');
  267.                 // WRONLY, CREATE_FILE, TRUNCATE
  268.                 // from http://lxr.mozilla.org/aviary101branch/source/nsprpub/pr/include/prio.h
  269.                 outputStream.init(this._runningTransactionFile, -1, 0644, 0);
  270.                 converter.writeString(this._next.map(function (r){return r.toXMLString().replace(/\n/g,"");}).join('\n')+"\n");
  271.                 converter.flush();
  272.                 outputStream.flush();
  273.                 outputStream.close();
  274.         // 2) clean up next
  275.             // Clean in-memory
  276.                 this._next=[];
  277.             // Clean next transaction file
  278.                 var outputStream = CL["@mozilla.org/network/file-output-stream;1"].createInstance( CI.nsIFileOutputStream);
  279.                 outputStream.init(this._nextTransactionFile, 0x02 | 0x08 | 0x20 , 0644, 0);
  280.                 outputStream.write("", 0);
  281.                 outputStream.flush();
  282.                 outputStream.close();
  283.     } else {
  284.         log.debug("Redo current request");
  285.     }
  286.     return this._running;
  287. }
  288.  
  289. CommandQueue.prototype._switchToNextRequests = function () {
  290.     this._running=[];
  291.     var outputStream = CL["@mozilla.org/network/file-output-stream;1"].createInstance( CI.nsIFileOutputStream);
  292.     outputStream.init(this._runningTransactionFile, 0x02 | 0x08 | 0x20 , 0644, 0);
  293.     outputStream.write("", 0);
  294.     outputStream.flush();
  295.     outputStream.close();
  296. }
  297.  
  298. CommandQueue.prototype.isEmpty = function() {
  299.     return (this._next.length == 0 && this._running.length == 0);
  300. }
  301.  
  302. CommandQueue.prototype._timer = CL['@mozilla.org/timer;1'].createInstance(CI.nsITimer);
  303.  
  304. // Wait in order to aggregate some requests and try to send 
  305. CommandQueue.prototype.flush = function () {
  306.     if (this.isRunning)
  307.         return log.debug("no flush because a request is running");
  308.     this._timer.initWithCallback(this, REQUEST_DELAY, this._timer.TYPE_ONE_SHOT);
  309. }
  310.  
  311. CommandQueue.prototype.notify = function () {
  312.     try {
  313.         this._timer.cancel();
  314.         this.send();
  315.     } catch(e) {
  316.         log.exception(e);
  317.     }
  318. }
  319.  
  320. CommandQueue.prototype.setSyncId = function() {
  321.     
  322.     if( !this.serverSyncId || this.serverSyncId.length == 0 ) {
  323.         YOONO_PREFS.set('prevsyncid',YOONO_PREFS.get('syncid') || '', PREFS.PREF_STRING);
  324.         YOONO_PREFS.set('syncid', yoono.bkm.getSyncId(), PREFS.PREF_STRING);
  325.     } else {
  326.         YOONO_PREFS.set('prevsyncid',this.serverSyncId, PREFS.PREF_STRING);
  327.         YOONO_PREFS.set('syncid', this.serverSyncId, PREFS.PREF_STRING);
  328.         this.serverSyncId = "";
  329.     }
  330.     //yoono.bkm.dump();
  331.     
  332. }
  333.  
  334. CommandQueue.prototype._replaceSyncId = function (syncid) {
  335.     this.serverSyncId=syncid;
  336. }
  337.  
  338. CommandQueue.prototype.nextPersistentCommandIsEmpty = function () {
  339.     
  340.     return ( !this._nextTransactionFile.exists() || this._nextTransactionFile.fileSize<=4);
  341.     
  342. }
  343.  
  344. CommandQueue.prototype.send = function () {
  345.     if (this.isRunning)
  346.         return log.debug("Not sending, because a request is running");
  347.     
  348.     var _this=this;
  349.     
  350.     var requests=this._getRequest();
  351.     if (requests.length==0)
  352.         return log.debug("No more requests to send!");
  353.     this.isRunning=true;
  354.     
  355.     // Encapsulate the request
  356.     if (!this._retryOnError)
  357.         this.setSyncId();
  358.     var xml = <server-script version="1.0"/>;
  359.         xml.appendChild( <context>
  360.                 {(YOONO_PREFS.get('userid')      ? <user-id> {YOONO_PREFS.get('userid')}  </user-id> : '')}
  361.                 {(YOONO_PREFS.get('syncid')      ? <sync-id> {YOONO_PREFS.get('syncid')} </sync-id> : '')}
  362.                 {(YOONO_PREFS.get('prevsyncid')  ? <prev-sync-id> {YOONO_PREFS.get('prevsyncid')} </prev-sync-id> : '')}
  363.             <locale>{yoono.utils.getLocale()}</locale>
  364.             <version>{yoono.utils.getExtensionVersion()}</version>
  365.             <client>xpi</client>
  366.         </context>);
  367.     for each(var r in requests) {
  368.         xml.appendChild(r);
  369.     }
  370.     log.info('Sending async POST on ' + YOONO_PREFS.get('serverurl') + 'linkserver \n' + xml.toXMLString().replace(/<user-id>(.*)<\/user-id>/gi,"<user-id>*************</user-id>") + "\n>>>>>>>>>>>> ");
  371.     
  372.     // Warm up ajax request
  373.     var data= 'script=' + encodeURIComponent( xml.toXMLString() );
  374.     
  375.     var xmlhttp = CL["@mozilla.org/xmlextras/xmlhttprequest;1"].createInstance(CI.nsIXMLHttpRequest);
  376.     
  377.     xmlhttp.open('POST', YOONO_PREFS.get('serverurl') + 'linkserver', true);
  378.     
  379.     xmlhttp.setRequestHeader("Cache-Control", "no-cache");
  380.     xmlhttp.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
  381.     
  382.     // Called on any type of error
  383.     function onError() {
  384.         try { xmlhttp.abort(); } catch(e) {}
  385.         
  386.         for each(var o in _this._observers) {
  387.             try {
  388.                 o.onRequestError(requests);
  389.             } catch(e) {
  390.                 log.exception(e);
  391.             }
  392.         }
  393.         
  394.         _this._retryOnError=true;
  395.         
  396.         // Then try to resend the current request some times after
  397.         var delayFunction={
  398.             notify : function (t) {
  399.                 _this.flush();
  400.             }
  401.         };
  402.         var timer = CL['@mozilla.org/timer;1'].createInstance(CI.nsITimer);
  403.         timer.initWithCallback(delayFunction, 6000, timer.TYPE_ONE_SHOT);
  404.         
  405.     }
  406.     
  407.     // Initialize a timer to timeout the request if necessary
  408.     var timeoutObserver = {
  409.         timer : CL['@mozilla.org/timer;1'].createInstance(CI.nsITimer),
  410.         delay : YOONO_PREFS.get('request.timeout') * 1000, // la pref est en secondes
  411.         start : function () {
  412.             this.timer.initWithCallback(this, this.delay, this.timer.TYPE_ONE_SHOT);
  413.         },
  414.         stop : function () {
  415.             this.timer.cancel();
  416.         },
  417.         notify : function (t) {
  418.             this.stop();
  419.             _this.isRunning=false;
  420.             
  421.             onError();
  422.         }
  423.     };
  424.     
  425.     // Called on success
  426.     xmlhttp.onload = function (event) {
  427.         try {
  428.             timeoutObserver.stop();
  429.             _this.isRunning=false;
  430.             
  431.             if((4 != event.target.readyState) || (200 != event.target.status)) {
  432.                 log.error(event.target.readyState+"/"+event.target.status);
  433.                 return onError();
  434.             }
  435.             
  436.             var data = new XML(xmlhttp.responseText.replace(/<\?xml.*?\?>\n?/g, ""));
  437.             
  438.             log.info('Receiving response \n' + data.toXMLString() + "\n<<<<<<<<<<<<< ");
  439.             
  440.             // Notify all observers and collect new requests 
  441.             // all new requests are added to the running requests list
  442.             var redo=false;
  443.             for each(var o in _this._observers) {
  444.                 var replace=false;
  445.                 try {
  446.                     replace = o.onRequestSuccess(requests,data);
  447.                 } catch(e) {
  448.                     log.exception(e);
  449.                 }
  450.                 if (replace) {
  451.                     if (replace.overrideSyncId)
  452.                         _this._replaceSyncId(replace.overrideSyncId);
  453.                     _this._replaceRunningRequest(replace.xml);
  454.                     redo=true;
  455.                     break;
  456.                 }
  457.             }
  458.             
  459.             if (!redo)
  460.                 _this._switchToNextRequests();
  461.             
  462.             _this._retryOnError=false;
  463.             _this.flush();
  464.             
  465.         } catch(e) {
  466.             log.exception(e);
  467.         }
  468.     };
  469.     
  470.     // Called on network error
  471.     xmlhttp.onerror = function (e) { // Error
  472.         try {
  473.             timeoutObserver.stop();
  474.             _this.isRunning=false;
  475.             
  476.             onError();
  477.             
  478.         } catch(e) {
  479.             log.exception(e);
  480.         }
  481.     };
  482.     
  483.     // Notify observers
  484.     for each(var o in this._observers) {
  485.         try {
  486.             o.onSendingRequest(requests);
  487.         } catch(e) {
  488.             log.exception(e);
  489.         }
  490.     }
  491.     
  492.     // Go go go!
  493.     xmlhttp.send(data);
  494.     
  495.     timeoutObserver.start();
  496.     
  497. }
  498.     
  499.  
  500.     
  501.  
  502. var commandQueueMgr = new CommandQueue();
  503.